#!/bin/perl -w
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
#-------------------------------------------------------
# generate_ese_stubs.pl
# Perl script to generate the DLL intercepts for the ESE API
#
# Input files:
# ese_apis_file_do_not_include.h
#
# Output files:
# staticcommon.cxx
# staticnarrow.cxx
# staticwiden.cxx
# staticcustom_temp.cxx
# loadcommon.cxx
# loadnarrow.cxx
# loadwiden.cxx
# loadcustom_temp.cxx
#
# Author SOMEONE
# Created 2004 September.
#-------------------------------------------------------
# TODO:
#
# -In the custom files, call a widening function. Then generation of the API
# can still be automated, but the magical widening function (which will
# know which fields to widen, which pointers are actually arrays, etc.)
# can be hand-written and placed in another file.
# -But that requires being able to specify the names of these magical
# parameters. Will need to modify the input header file format.
#
#-Add other abstract types (e.g. JET_INDEXCREATE size changing in Longhorn)
#-Add functionality for version querying
#-Add a section of 'always LoadLibrary()ed', for new APIs since Pt/XP.
#
# -Fix the Unicode files so they compile properly (and remove the
# ESETESTWIDE check from the DIRS file)
#-------------------------------------------------------


use strict;
use Class::Struct;
use Getopt::Long;
use IO::Handle;
use FileHandle;

# friendlier names ( e.g. $ERRNO instead of $! )
use English;

use vars qw/ $g_dbg $g_dbg2 $g_strCommonHeader $g_strDoNotEdit $g_strOkToEdit $g_idxProviderThunk/;

local $g_dbg = 1;
local $g_dbg2 = 0;

# Used to keep track of an index in the  thunk array
local $g_idxProviderThunk = 0;

local $g_strCommonHeader =
'// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// Generated by generate_ese_stubs.pl
#include "ese_common.hxx"
#include "esetest_bounce.hxx"
#include <stdlib.h>	// for rand()
#include <strsafe.h>
#include <windows.h>
';

local $g_strDoNotEdit =
'// Do NOT edit this file! It can (and will) be overwritten.
';

local $g_strOkToEdit =
'// This file was generated, but you may edit it. If generate_ese_stubs.pl is
// run again, it will create a \'_temp\' file instead.
';

# Prototypes
sub ParseEseApi( $ );
sub ParseApi( $$$$$$$$$ );
sub ProcessApi( $$$$$$$$$$ );
sub main();

main();


sub PrintHelperScreen()
{
	# Console is 8-space tabs. SI is 4-space. So it doesn't look lined up, but it is.
	print <<HELP;
generate_ese_stubs.pl [options]-- creates stubs for abstracting ESE calls in a DLL
	-help				this screen
	-?				ditto
HELP
}


sub main()
{
	my $retCode		= 0;
	my $helpRequested	= 0;
	my $strError	= '';
	my $apiFile = "ese_apis_file_do_not_include.h";

	GetOptions(
			'help|?'	=> \$helpRequested,
			);

	if ( $helpRequested ) {
		PrintHelperScreen();
		return -5;
	}

	$retCode = ParseEseApi( $apiFile );

Cleanup:
	if ( $strError ) {
		print "Errors encountered:\n$strError";
		if ( 0 == $retCode ) {
			$retCode = -1003;
		}
	}

	return $retCode;
}

sub PrintShimLoadLibraryCall($$$$)
{
	my $handle = shift @_;
	my $strApiName = shift @_;
	my $strBareParams = shift @_;
	my $strAdditionalIndent = shift @_;

	$handle->print(
		"\n".
		"\tif ( FEsetestInjectFault( \"$strApiName\", g_rgerr$strApiName, sizeof( g_rgerr$strApiName ) / sizeof( g_rgerr${strApiName}[ 0 ] ), &err ) )\n".
		"\t{\n".
		"\t}\n".
		"\telse\n".
		"\t{\n".
		"\t\tFUNCTION_TYPE Function = AVRF_GET_ORIGINAL_EXPORT( VfProviderEsentThunks, VF_PROVIDER_ESENT_HOOK_$strApiName );\n".
		"\t\terr = ( * Function )( $strBareParams );\n".
		"\n".
		"\t\tEsetestMaybeLogAndMaybeVerifyInjectedFault( \"$strApiName\", g_rgerr$strApiName, ARRAYSIZE( g_rgerr$strApiName ), err );\n".
		"\t}\n".
		"\n"
	);


}


sub ProcessShimApi( $$$$$$$ )
#	$happverifiershimFile
#	$happverifiershimHeaderFile
#	$strApiType
#	$strApiName
#	$strApiSignature
#	\%hashParamsToWiden
#	\%hashFunctionsToWiden
#	\@rgstrApiParameters
#	\@rgstrApiParametersNoSal
{
	my $retCode  = 0;

	my $happverifiershimFile = shift @_;
	my $happverifiershimHeaderFile = shift @_;
	my $strApiType = shift @_;
	my $strApiName = shift @_;
	my $strApiSignature = shift @_;
#		my $refhashParamsToWiden = shift @_;
#		my $refhashFunctionsToWiden = shift @_;
	my $refrgstrApiParameters = shift @_;
	my $refrgstrApiParametersNoSal = shift @_;

	my @rgstrApiParameters = @$refrgstrApiParameters;
	my @rgstrApiParametersNoSal = @$refrgstrApiParametersNoSal;

	my $strApiWideSignature = $strApiSignature;
	$strApiWideSignature =~ s/char/WCHAR/g;
	$strApiWideSignature =~ s/JET_PSTR/JET_PWSTR/g;
	$strApiWideSignature =~ s/JET_PCSTR/JET_PCWSTR/g;
	$strApiWideSignature =~ s/sz/wsz/g;

	my $param;
	my @rgstrNarrowParams;

	my @rgBareParams;
	my $strBareParams = "";

	my $strParams = "";

	# Create a list of bare-parameters (no types);
	# and also a string of parameters (with types).
	foreach $param ( @rgstrApiParameters )
	{
		$strParams .= ",\n\t$param";
	}

	foreach $param ( @rgstrApiParametersNoSal )
	{
		$param =~ m/(\w+)$/;
		push @rgBareParams, $1;
		$strBareParams .= ", $1";
	}

	# Only remove the initial ", " if it was modified
	if ( $strBareParams ne "" )
	{
		$strBareParams =~ m/^, (.*)$/;
		$strBareParams = $1;
	}

	# Sometimetimes the signature is 'void' which makes it difficult to call.
	if ( $strBareParams =~ m/^\s*void\s*$/ )
	{
		 $strBareParams = "";
	}

	# Only remove the initial ", " if it was modified
	if ( $strParams ne "" )
	{
		$strParams =~ m/^,\n\t(.*)$/s;
		$strParams = $1;
	}

#	my $Opening = "\nJET_ERR\nBounce$strApiName(\n";
#	$happverifiershimFile->print( $Opening );
#	$handleLoad->print( $Opening );
#
#	foreach $param ( @rgstrApiParameters )
#	{
#		$happverifiershimFile->print( "\t$param,\n" );
#		$handleLoad->print( "\t$param,\n" );
#	}
#
#	my $strSigClose = ")\n{\n\tJET_ERR		err = JET_errSuccess;\n";
#
#	$happverifiershimFile->print( $strSigClose );
#	$handleLoad->print( $strSigClose );

	my $strPrototype = "\nJET_ERR\nJET_API\nVfHook$strApiName(\n\t$strParams\n)";
	my $Opening = "$strPrototype\n{\n\tJET_ERR		err = JET_errSuccess;\n";

	$happverifiershimHeaderFile->print( "\n\n#define VF_PROVIDER_ESENT_HOOK_$strApiName $g_idxProviderThunk\n" );
	++$g_idxProviderThunk;

	$happverifiershimHeaderFile->print( "\n$strPrototype\n;\n" );
	$happverifiershimFile->print( $Opening );

#	PrintLoadLibraryVars( $happverifiershimFile, $strApiName, $strApiSignature );
	$happverifiershimFile->print(
		"\n\ttypedef JET_ERR ( JET_API *FUNCTION_TYPE ) ( $strApiSignature );\n\n"
	);

	PrintShimLoadLibraryCall( $happverifiershimFile, $strApiName, $strBareParams, "" );


	my $strCleanup = "\tgoto Cleanup;\t// Need to have the explicit goto in case the function hasn't referenced 'Cleanup' yet.\nCleanup:\n";
#	$happverifiershimFile->print( $strCleanup );


	my $strReturn = "\n\treturn err;\n}\n\n" .
	"//---------------------------------------------------\n\n";
	$happverifiershimFile->print( $strReturn );

	return $retCode;
}



sub PrintAppVerifierHeader($)
{
	my $happverifiershimFile = shift @_;
	$happverifiershimFile->print(
		"// Copyright (c) Microsoft Corporation.\n".
		"// Licensed under the MIT License.\n".
		"\n".
		"// Generated by generate_ese_stubs.pl\n".
		"\n".
		"// Do NOT edit this file! It can (and will) be overwritten.\n".
		"\n".
		"\n".
		"\n".
		"#include \"precomp.h\"\n".
		"#include \"vfdecls.h\"\n".
		"#include \"esentshim.hxx\"\n".
		"#include \"eseshimerrors.hxx\"\n".
		"#include \"esentshimcommon.hxx\"\n".
		"\n".
		"\n".
		"#include <esent.h>\n".
		"\n".
		"\n".
		"\n".
		"////////////////////\n".
		""
	);

}

sub PrintAppVerifierFooter($$)
# $happverifiershimFile
# \@listApis
{
	my $happverifiershimFile = shift @_;
	my $reflistApis = shift @_;
	my $strApi;

	$happverifiershimFile->print(
		"\n".
		"\n".
		""
	);

	$happverifiershimFile->print(
#		"\t// Create the array to hook each API\n".
#		"\tRTL_VERIFIER_THUNK_DESCRIPTOR VfProviderEsentThunks [] = {\n"
	);

	foreach $strApi ( @$reflistApis )
	{
		$happverifiershimFile->print(
#			"\t\t{ \"$strApi\", NULL, VfHook$strApi }\n"
		);
	}

	$happverifiershimFile->print(
#		"\t\t{ NULL, NULL, NULL }\n".
#		"\t}\n"
	);

#	$happverifiershimFile->print(
#		"//----------------------------------------------\n".
#		"void\n".
#		"EsentShimLogApiCall(\n".
#		"	_In_	PCSTR			szApi\n".
#		")\n".
#		"{\n".
#		"	VLOG( VLOG_LEVEL_INFO,\n".
#		"		 VLOG_ESENTSHIM_APICALL,\n".
#		"		 \"Called %s()\",\n".
#		"		 szApi\n".
#		"		 );\n".
#		"}\n".
#		"\n".
#		"//----------------------------------------------\n".
#		"void\n".
#		"EsentShimLogInjectedError(\n".
#		"	_In_	PCSTR			szApi,\n".
#		"	_In_	JET_ERR 		errInjected\n".
#		")\n".
#		"{\n".
#		"	VLOG( VLOG_LEVEL_INFO,\n".
#		"		 VLOG_ESENTSHIM_INJECTERROR,\n".
#		"		 \"%s(), injected %d\",\n".
#		"		 szApi, errInjected\n".
#		"		 );\n".
#		"}\n".
#		"\n".
#		"\n".
#		""
#	);

}

sub PrintAppVerifierHeaderFileHeader($)
{
	my $happverifiershimFile = shift @_;
	$happverifiershimFile->print(
		"// Copyright (c) Microsoft Corporation.\n".
		"// Licensed under the MIT License.\n".
		"\n".
		"// Generated by generate_ese_stubs.pl\n".
		"\n".
		"// Do NOT edit this file! It can (and will) be overwritten.\n".
		"\n".
		"\n".
		"\n".
		"\n".
		"#ifndef _VFHOOKS_H_INCLUDED_\n".
		"#define _VFHOOKS_H_INCLUDED_\n".
		"\n".
		"#include <esent.h>\n".
		"\n".
		"extern RTL_VERIFIER_THUNK_DESCRIPTOR VfProviderEsentThunks[];\n".
		"\n".
		"\n".
		"//\n".
		"// Hook declarations.\n".
		"//\n".
		"\n".
		"\n".
		""
	);

}

sub PrintAppVerifierHeaderFileFooter($)
# $happverifiershimFile
{
	my $happverifiershimFile = shift @_;
	my $strApi;

	$happverifiershimFile->print(
		"#define VF_PROVIDER_ESENT_HOOK_MAXIMUM_INDEX $g_idxProviderThunk\n".
		"\n".
		"#endif // #ifndef _VFHOOKS_H_INCLUDED_\n".
		"\n".
		""
	);
	++$g_idxProviderThunk;
}

## UNUSED
## sub PrintAppVerifierHeaderFile( $$ )
## # $happverifiershimHeaderFile
## # \%$hashApis
## {
## 	my $happverifiershimHeaderFile = shift @_;
## 	my $refhashApis = shift @_;
##
## 	my $strApi;
##
## 	$happverifiershimHeaderFile->print(
## 		"\n".
## 		"\n".
## 		"APIHOOK_ENUM_BEGIN\n"
## 	);
##
## 	foreach $strApi ( keys( %$refhashApis ) )
## 	{
## 		$happverifiershimHeaderFile->print(
## 			"\tAPIHOOK_ENUM_ENTRY( $strApi )\n"
## 		);
## 	}
##
## 	$happverifiershimHeaderFile->print(
## 		"APIHOOK_ENUM_END\n".
## 		"\n"
## 	);
##
## 	$happverifiershimHeaderFile->print(
## 	);
##
## }

sub PrintAppVerifierThunkArray( $$ )
# $happverifiershimFile
# \%$listApis
{
	my $happverifiershimFile = shift @_;
	my $reflistApis = shift @_;

	my $strApi;

	$happverifiershimFile->print(
		"\n".
		"// Create the array to hook each API\n".
		"RTL_VERIFIER_THUNK_DESCRIPTOR VfProviderEsentThunks [] = {\n"
	);

	foreach $strApi ( @$reflistApis )
	{
		$happverifiershimFile->print(
			"\t{ \"$strApi\",\t\tNULL,\t\tVfHook$strApi },\n"
		);
	}

	$happverifiershimFile->print(
		"\t{ NULL, NULL, NULL },\n".
		"};\n".
		"\n".
		"// Confirm that the VF_PROVIDER_ESENT_HOOK_MAXIMUM_INDEX constant is correct.\n".
		"C_ASSERT( LENGTH_OF( VfProviderEsentThunks) == VF_PROVIDER_ESENT_HOOK_MAXIMUM_INDEX + 1);\n".
		"\n"
	);

	$happverifiershimFile->print(
	);

}


sub ParseEseApi( $ )
{
	my $apiFile = shift @_;
	if ( $g_dbg ) { print "Entered ParseEseApi( $apiFile );\n" ; }
	my $retCode = 0;

	my $strApiType;
	my $strApiName;
	my $strApiSignature;
	my @rgstrApiParameters;
	my @rgstrApiParametersNoSal;

	my $staticCommonFileName	= "staticcommon.cxx";
	my $staticNarrowFileName	= "staticnarrow.cxx";
	my $staticWidenFileName		= "staticwiden.cxx";
	my $staticCustomFileName	= "staticcustom_temp.cxx";
	my $loadCommonFileName		= "loadcommon.cxx";
	my $loadNarrowFileName		= "loadnarrow.cxx";
	my $loadWidenFileName		= "loadwiden.cxx";
	my $loadCustomFileName		= "loadcustom_temp.cxx";
	my $appverifiershimFileName		= "esentshim.cxx";
	my $strExportFileName		= "esetest_bounce.hxx";
	my $strverifiershimHeaderFileName	= "esentshim.hxx";

	my $hfileApi;
	my $hdefFile;
	my $defFileName = "esetest_bounce.def";

	my $hstaticCommonFile;
	my $hstaticNarrowFile;
	my $hstaticWidenFile;
	my $hstaticCustomFile;
	my $hloadCommonFile;
	my $hloadNarrowFile;
	my $hloadWidenFile;
	my $hloadCustomFile;
	my $happverifiershimFile;
	my $hExportHeaderFile;
	my $happverifiershimHeaderFile;

	my @rgWritableHandles = (
		\$hstaticCommonFile,
		\$hstaticNarrowFile,
		\$hstaticWidenFile,
		\$hstaticCustomFile,
		\$hloadCommonFile,
		\$hloadNarrowFile,
		\$hloadWidenFile,
		\$hloadCustomFile,
		\$hExportHeaderFile,
		);

	my @rgOtherWritableHandles = (
		\$happverifiershimFile,
		\$happverifiershimHeaderFile,
		);

	my @rghDoNotEdit = (
		\$hstaticCommonFile,
		\$hstaticNarrowFile,
		\$hstaticWidenFile,
#		\$hstaticCustomFile,
		\$hloadCommonFile,
		\$hloadNarrowFile,
		\$hloadWidenFile,
#		\$hloadCustomFile,
		\$happverifiershimFile,
		\$hExportHeaderFile,
		\$happverifiershimHeaderFile,
	);

	my @rghOkToEdit = (
		\$hstaticCustomFile,
		\$hloadCustomFile,
	);

	my %hashParamsToWiden;
	my %hashFunctionsToWiden	= ();
	my %hashNewTypeToOldType	= ();
	my @listApis				= ();

	my $api;


#	struct( outputfiles => {
#		handleFile => '*$',
#		strFileName => '$',
#	});
#
#	my @rgOutputFiles = ( outputfiles->new( handleFile => \$hstaticCommonFile, strFileName => $staticCommonFileName ) );


#	local $INPUT_RECORD_SEPARATOR = ';';
	print "Opening $apiFile...\n";
#	if ( ! open( APIFILE, '+<', $apiFile ) )
	$hfileApi = new FileHandle $apiFile, "r";
	if ( ! defined $hfileApi )
	{
		print "Can't open $apiFile for reading: $ERRNO\n";
		$retCode = $ERRNO;
		goto Cleanup;
	}

	$hdefFile = new FileHandle $defFileName, "w";
	if ( ! defined $hdefFile )
	{
		print "Can't open $defFileName for reading: $ERRNO\n";
		$retCode = $ERRNO;
		goto Cleanup;
	}


	# delimt the records by semicolon
	IO::Handle->input_record_separator( ';' );
#	$hfileApi->input_record_separator( ';' );

	# Open the output files

#	foreach my $outputfile ( @rgOutputFiles )
#	{
#		$outputfile->handleFile = new FileHandle $outputfile->strFileName, "r";
#		if ( ! defined $outputfile->handleFile )
#		{
#			print "Can't open $outputfile->strFileName for reading: $ERRNO\n";
#			$retCode = $ERRNO;
#			goto Cleanup;
#		}
#	}

	$hstaticCommonFile = new FileHandle $staticCommonFileName, "w";
	if ( ! defined $hstaticCommonFile )
	{
		print "Can't open $staticCommonFileName for writing: $ERRNO\n";
		$retCode = $ERRNO;
		goto Cleanup;
	}

	$hstaticNarrowFile = new FileHandle $staticNarrowFileName, "w";
	if ( ! defined $hstaticNarrowFile )
	{
		print "Can't open $staticNarrowFileName for writing: $ERRNO\n";
		$retCode = $ERRNO;
		goto Cleanup;
	}

	$hstaticWidenFile = new FileHandle $staticWidenFileName, "w";
	if ( ! defined $hstaticWidenFile )
	{
		print "Can't open $staticWidenFileName for writing: $ERRNO\n";
		$retCode = $ERRNO;
		goto Cleanup;
	}

	$hstaticCustomFile = new FileHandle $staticCustomFileName, "w";
	if ( ! defined $hstaticCustomFile )
	{
		print "Can't open $staticCustomFileName for writing: $ERRNO\n";
		$retCode = $ERRNO;
		goto Cleanup;
	}

	$hloadCommonFile = new FileHandle $loadCommonFileName, "w";
	if ( ! defined $hloadCommonFile )
	{
		print "Can't open $loadCommonFileName for writing: $ERRNO\n";
		$retCode = $ERRNO;
		goto Cleanup;
	}

	$hloadNarrowFile = new FileHandle $loadNarrowFileName, "w";
	if ( ! defined $hloadNarrowFile )
	{
		print "Can't open $loadNarrowFileName for writing: $ERRNO\n";
		$retCode = $ERRNO;
		goto Cleanup;
	}

	$hloadWidenFile = new FileHandle $loadWidenFileName, "w";
	if ( ! defined $hloadWidenFile )
	{
		print "Can't open $loadWidenFileName for writing: $ERRNO\n";
		$retCode = $ERRNO;
		goto Cleanup;
	}

	$hloadCustomFile = new FileHandle $loadCustomFileName, "w";
	if ( ! defined $hloadCustomFile )
	{
		print "Can't open $loadCustomFileName for writing: $ERRNO\n";
		$retCode = $ERRNO;
		goto Cleanup;
	}

	$happverifiershimFile = new FileHandle $appverifiershimFileName, "w";
	if ( ! defined $happverifiershimFile )
	{
		print "Can't open $appverifiershimFileName for writing: $ERRNO\n";
		$retCode = $ERRNO;
		goto Cleanup;
	}

	$hExportHeaderFile = new FileHandle $strExportFileName, "w";
	if ( ! defined $hExportHeaderFile )
	{
		print "Can't open $strExportFileName for writing: $ERRNO\n";
		$retCode = $ERRNO;
		goto Cleanup;
	}

	$happverifiershimHeaderFile = new FileHandle $strverifiershimHeaderFileName, "w";
	if ( ! defined $happverifiershimHeaderFile )
	{
		print "Can't open $strverifiershimHeaderFileName for writing: $ERRNO\n";
		$retCode = $ERRNO;
		goto Cleanup;
	}


	# Need to prevent inclusion of itself before $g_strCommonHeader is printed.
	$hExportHeaderFile->print(
		"#pragma once\n".
		"#ifndef ESETEST_BOUNCE_HXX_INCLUDED\n".
		"\n// Remove name decoration\n".
		"#ifdef	__cplusplus\n" .
		"extern \"C\" {\n" .
		"#endif\n\n"
	);

	my $handle;
	# OK, everything's is opened. Now write the common header.
	foreach $handle ( @rgWritableHandles )
	{
		$$handle->print( $g_strCommonHeader );
	}

	foreach $handle ( @rghDoNotEdit )
	{
		$$handle->print( "$g_strDoNotEdit" );
	}

	foreach $handle ( @rghOkToEdit )
	{
		$$handle->print( "$g_strOkToEdit" );
	}

	$hdefFile->print( ";; The following exports have been generated by generate_ese_stubs.pl\n\n" );

	PrintAppVerifierHeader( $happverifiershimFile );
	PrintAppVerifierHeaderFileHeader( $happverifiershimHeaderFile );

	while ( $api = <$hfileApi> )
	{
		%hashParamsToWiden = ();
		# Do NOT clear %hashFunctionsToWiden -- we need it at the end.
		$retCode = ParseApi( $api, \$strApiType, \$strApiName, \$strApiSignature, \@rgstrApiParameters, \@rgstrApiParametersNoSal, \%hashParamsToWiden, \%hashFunctionsToWiden, \%hashNewTypeToOldType );
		if ( $retCode < 0 )
		{
			print( "Failed to parse an API, error was $retCode. The API was [$api].\n" );
			goto Cleanup;
		}
		if ( $strApiType eq "ignore" )
		{
			next;
		}

		$hdefFile->print( "\t$strApiName","A=Bounce$strApiName\n" );
		$hdefFile->print( "\t$strApiName=Bounce$strApiName\n" );

		$listApis[ ++$#listApis ] = $strApiName;

		if ( $strApiType =~ m/neutral/ )
		{
			ProcessApi( $hstaticCommonFile, $hloadCommonFile, $hExportHeaderFile, $strApiType, $strApiName, $strApiSignature, \%hashParamsToWiden, \%hashFunctionsToWiden, \@rgstrApiParameters, \@rgstrApiParametersNoSal );
		}
		elsif ( $strApiType =~ m/widen/ )
		{
			ProcessApi( $hstaticWidenFile, $hloadWidenFile, $hExportHeaderFile, $strApiType, $strApiName, $strApiSignature, \%hashParamsToWiden, \%hashFunctionsToWiden, \@rgstrApiParameters, \@rgstrApiParametersNoSal );
			ProcessApi( $hstaticNarrowFile, $hloadNarrowFile, undef, "narrow", $strApiName, $strApiSignature, \%hashParamsToWiden, \%hashFunctionsToWiden, \@rgstrApiParameters, \@rgstrApiParametersNoSal );
		}
		elsif ( $strApiType =~ m/custom/ )
		{
			# Concatenating with "widen" is a hack to get the parameter futzing working.
			ProcessApi( $hstaticCustomFile, $hloadCustomFile, $hExportHeaderFile, $strApiType . "widen", $strApiName, $strApiSignature, \%hashParamsToWiden, \%hashFunctionsToWiden, \@rgstrApiParameters, \@rgstrApiParametersNoSal );
		}
		else
		{
			print "$strApiType is an unrecognized API type!\n";
		}

		ProcessShimApi( $happverifiershimFile, $happverifiershimHeaderFile, $strApiType, $strApiName, $strApiSignature, \@rgstrApiParameters, \@rgstrApiParametersNoSal );
		print "$strApiName\n";
#		print "JET_ERR\ng_rgerr${strApiName}[] =\n{\n\tJET_errInvalidParameter,\n}\n;\n\n";
	}

	

	# added by SOMEONE
	# I need to make sure FEsetestWidenParameters never returns
	# widen for the narrow case.  Therefore, we create a new API, which is actually (unfortunately )
	# not related to Jet, FEsetestAlwaysNarrow(), in loadnarrow.cxx and loadwiden.cxx.

	my $strWidenParametersPercentAPI = "FEsetestAlwaysNarrow"; 

	# for hloadWideFile
	$hloadWidenFile->print(
		"\nbool $strWidenParametersPercentAPI()". 	
		 "\n{".
		 "\n\treturn false;".
		 "\n}"
	);

	#for hloadNarrowFile
	$hloadNarrowFile->print(
		"\nbool $strWidenParametersPercentAPI()". 	
		 "\n{".
		 "\n\treturn true;".
		 "\n}"
	);
	my $str = "\n\n//---------------------------------------------------\n\n";

	$hloadNarrowFile->print(
		$str
		);

	$hloadWidenFile->print(
		$str
		);

	PrintAppVerifierFooter( $happverifiershimFile, \@listApis );
	PrintAppVerifierThunkArray( $happverifiershimFile, \@listApis );
	PrintAppVerifierHeaderFileFooter( $happverifiershimHeaderFile );

	# deadcode
	if ( 0 && %hashFunctionsToWiden )
	{
		# Generate the prototypes for the widening functions
		$hExportHeaderFile->print(
			"\n//--------------------\n".
			"\t// Prototypes for the widening functions:\n".
			"\n"
		);

		my $strType;
		foreach $strType ( keys( %hashFunctionsToWiden ) )
		{
			#print "A new type is $strType\n";
			#print "It points to " . $strType . " and ". $hashFunctionsToWiden{ $strType } . "\n";
			$hExportHeaderFile->print(
				"$strType\n".
				$hashFunctionsToWiden{ $strType } . "\n".
				"(\n".
				"\t" . $hashNewTypeToOldType{ $strType } . "\n" .
				")\n".
				"\n"
			);
		}

		$hExportHeaderFile->print(
			"\n//--------------------\n".
			"\n"
		);
	}


	$hExportHeaderFile->print(
		"\n// Remove name decoration\n".
		"#ifdef	__cplusplus\n" .
		"}\n" .
		"#endif\n".
		"#endif\t// !ESETEST_BOUNCE_HXX_INCLUDED\n"
	);



	print "OK\n";


Cleanup:

	if ( defined $hfileApi )
	{
		$hfileApi->close;
	}

	if ( defined $hdefFile )
	{
		$hdefFile->close();
	}

	foreach $handle ( @rgWritableHandles )
	{
		if ( defined $$handle )
		{
			$$handle->close;
		}
	}

	foreach $handle ( @rgOtherWritableHandles )
	{
		if ( defined $$handle )
		{
			$$handle->close;
		}
	}


	return $retCode;
}

sub ParseWidenArgs( $$$$ )
# IN $strToWiden
# OUT %hashParamsToWiden -- hash table of the parameters that need to be widened
#					e.g. "szInstanceName" -> "wchar_t*",
# OUT %hashFunctionsToWiden -- hash table of the function names that widen the particular types.
#					e.g. "wchar_t*" -> "EsetestWidenString"
# OUT %hashNewTypeToOldType -- hash table of the new types to the old types
#					e.g. "wchar_t*" -> "char*"
{
	my $strToParse = shift @_;
	my $refhashParamsToWiden = shift @_;
	my $refhashWidenFunctions = shift @_;
	my $refhashNewTypeToOldType = shift @_;
	my $retCode = 0;
	my ($strNewType, $strOldType, $strVarName, $strFunction, $strFunctionCleanup );

	if ( defined $strToParse && "" ne $strToParse )
	{
#		print "Trying to parse [$strToParse]\n";
		while ( $strToParse =~ m/(\S+)\s+(\S+)\s+(\w+)\s+(\w+)\s*/g )
		{
			$strNewType = $1;
#			$strOldType = $2;
			$strVarName = $2;
			$strFunction = $3;
			$strFunctionCleanup = $4;

#			print "strType is $strNewType, strVarName is $strVarName, strFunction is $strFunction\n";
			$$refhashParamsToWiden{ $strVarName } = $strNewType;
			$$refhashWidenFunctions{ $strNewType } = [ $strFunction, $strFunctionCleanup ];
#			$$refhashNewTypeToOldType{ $strNewType } = $strOldType;
		}
		if ( !defined $strNewType )
		{
			print "ParseWidenArgs() failed to make a match! The input was [$strToParse]\n";
			$retCode = -5;
		}
	}

	return $retCode;
}

sub ParseApi( $$$$$$$$$ )
# /* $api, \$apiType, \$strApiName, \$strApiSignature, \@rgstrApiParameters*/;
# IN $api -- the raw field from the file. e.g.
# 				n
#				JET_ERR JET_API JetInit2( JET_INSTANCE *pinstance, JET_GRBIT grbit );
# OUT $apiType -- the type of API, ignore, neutral, widen, custom
# OUT $strApiName -- e.g. JetInit2
# OUT $strApiSignature -- scalar of parameters (including types)
#					e.g. 'JET_INSTANCE *pinstance, JET_GRBIT grbit '
# OUT @rgstrApiParameters -- list of parameters (inlcuding types)
#					e.g. ( 'JET_INSTANCE *pinstance', 'JET_GRBIT grbit ' )
# OUT @rgstrApiParametersNoSal -- list of parameters (inlcuding types)
#					e.g. ( 'JET_INSTANCE *pinstance', 'JET_GRBIT grbit ' )
# OUT %hashParamsToWiden -- hash table of the parameters that need to be widened
#					e.g. "szInstanceName" -> "wchar_t*",
# OUT %hashFunctionsToWiden -- hash table of the function names that widen the particular types.
#					e.g. "wchar_t*" -> "EsetestWidenString"
# OUT %hashNewTypeToOldType -- hash table of the new types to the old types
#					e.g. "wchar_t*" -> "char*"
{
	my $retCode	 = 0;
	my $api = shift @_;
	my $refapiType = shift @_;
	my $refstrApiName = shift @_;
	my $refstrApiSignature = shift @_;
	my $refrgstrApiParameters = shift @_;
	my $refrgstrApiParametersNoSal = shift @_;
	my $refhashParamsToWiden = shift @_;
	my $refhashWidenFunctions = shift @_;
	my $refhashNewTypeToOldType = shift @_;

	$$refapiType = "ignore";
	$$refstrApiName = "not_set";
	$$refstrApiSignature = "not_set_param";
	@$refrgstrApiParameters = ( "not_set_list" );
	@$refrgstrApiParametersNoSal = ( "not_set_list" );

	if ( $api =~ m/^\s*\// )
	{
		return 0;
	}


	if ( $api =~ m/^\s*$/ )
	{
		return 0;
	}


	if ( $api =~ m/^		# beginning of buffer
		\s*					# leading white space
		(\w+)\s+			# type
		((?:\S+\s+\S+\s+\w+\s+\w+\s*)*)	# custom widener
		JET_ERR\s+JET_API\s+	# return type
		(\w+)				# API name
		\s*\((				# start of function signature
		[^;]*				# contents of sig (SAL includes parens)
		)\)\s*				# closing parens
		;\s*				#
		$					# end of buffer
		/xs )
		{

			$$refapiType = $1;

			if ( $$refapiType eq "n" )
			{
				$$refapiType = "neutral";
			}
			elsif ( $$refapiType eq "w" )
			{
				$$refapiType = "widen";
			}
			elsif ( $$refapiType eq "c" )
			{
				$$refapiType = "custom";
			}
			else
			{
				print "Invalid API type listed: [$$refapiType]\n";
				$retCode = -6;
				goto Cleanup;
			}

			if ( defined $2 )
			{
				$retCode = ParseWidenArgs( $2, $refhashParamsToWiden, $refhashWidenFunctions, $refhashNewTypeToOldType );
				if ( $retCode != 0 )
				{
					print "ParseWidenArgs( $2, $refhashParamsToWiden, $refhashWidenFunctions ) returned $retCode!\n";
					goto Cleanup;
				}
			}

			$$refstrApiName = $3;
			# print "Matched $$refstrApiName!\n";
			$$refstrApiSignature = $4;

			# Remove the SAL annotations for when we call the function. The text inside parens confuses things.
			my $strApiSigNoSal = $4;
			$strApiSigNoSal =~ s/__\w+\([^)]+\)//g;

			# Split the parameters by splitting out the semi-colon
			my @rgstrParams = split /,\s*/, $$refstrApiSignature;
			my @rgstrParamsNoSal = split /,\s*/, $strApiSigNoSal;
			if ( $g_dbg2 ) { print "rgstrParams is @rgstrParams\n"; }
			@$refrgstrApiParameters = grep( !/^\s*$/, @rgstrParams );
			@$refrgstrApiParametersNoSal = grep( !/^\s*$/, @rgstrParamsNoSal );
			if ( $g_dbg2 ) { print "\trefrgstrApiParameters is @$refrgstrApiParameters\n"; }
			if ( $g_dbg2 ) { print "\trefrgstrApiParametersNoSal is @$refrgstrApiParametersNoSal\n"; }

			# Strip the leading/trailing whitespace.
			# Also count how many bytes the API will take.
			my $param;
			foreach $param ( @$refrgstrApiParameters )
			{
				$param =~ m/^\s*/;
				$param = $';
				$param =~ m/\s*$/;
				$param = $`;
			}
			if ( $g_dbg2 ) { print "\t\tpost-strip, refrgstrApiParameters is @$refrgstrApiParameters\n"; }

			foreach $param ( @$refrgstrApiParametersNoSal )
			{
				$param =~ m/^\s*/;
				$param = $';
				$param =~ m/\s*$/;
				$param = $`;
			}
			if ( $g_dbg2 ) { print "\t\tpost-strip, refrgstrApiParametersNoSal is @$refrgstrApiParametersNoSal\n"; }
		}
		else {
			print "Does not match!\n";
			$retCode = -3;
		}

Cleanup:
	return $retCode;
}


sub PrintWidenSpecialVariable( $$$ )
#	$handleOutput
#	$refhashParamsToWiden
#	$refhashFunctionsToWiden
{
	my $handleOutput = shift @_;
	my $refhashParamsToWiden = shift @_;
	my $refhashFunctionsToWiden = shift @_;
	my $strVariables;

	$handleOutput->print( "\n\t// Declare the variables\n" );
	foreach $strVariables ( keys( %$refhashParamsToWiden ) )
	{
#		print "A type is $strVariables\n";
		my $strType = $$refhashParamsToWiden{ $strVariables };
		my $strVariableToWiden = "COULD_NOT_FIND_IT";
		if ( $strVariables =~ m/^([^,]+)/ )
		{
			$strVariableToWiden = $1;
		}

#		print "It points to " . $strType . " and ". $$refhashFunctionsToWiden{ $strType } . "\n";
		$handleOutput->print(
			"\t$strType\tw$strVariableToWiden\t= NULL;\n"
		);
	}

	$handleOutput->print( "\n".
		"\tif ( fWiden )\n".
		"\t{\n".
		"\t\t// Widen the variables\n"
	);
	foreach $strVariables ( keys( %$refhashParamsToWiden ) )
	{
		my $strType = $$refhashParamsToWiden{ $strVariables };
		my $function = $$refhashFunctionsToWiden{ $strType }[ 0 ];

		# Ensure the parameters have a space after the commas.
		$strVariables =~ s/,\s*/, /;

		my $strVariableToWiden = "COULD_NOT_FIND_IT";
		if ( $strVariables =~ m/^([^,]+)/ )
		{
			$strVariableToWiden = $1;
		}

		#if ( $function ne "EsetestWidenStringWithLength" )

		$handleOutput->print(
			"\t\tif( NULL != $strVariableToWiden ) \n".
			"\t\t{\n" .
			"\t\t\tw$strVariableToWiden = $function( __FUNCTION__, $strVariables );\n".
			"\t\t\tif ( NULL == w$strVariableToWiden )\n" .
			"\t\t\t{\n".
			"\t\t\t\ttprintf( \"%s(): Failed to widen %s calling %s\" CRLF, __FUNCTION__, \"$strVariableToWiden\", \"$function\" );\n" .
			"\t\t\t\terr = JET_errOutOfMemory;\n" .
			"\t\t\t\tgoto Cleanup;\n" .
			"\t\t\t}\n" .
			"\t\t}\n" .
			"\n" 
		);

			#come back here
			#$handleOutput->print(
			#	"\t\tw$strVariableToWiden = $function( __FUNCTION__, $strVariables );\n".
			#	"\t\tif ( NULL == w$strVariableToWiden )\n" .
			#	"\t\t{\n".
			#		"\t\t\ttprintf( \"%s(): Failed to widen %s calling %s\" CRLF, __FUNCTION__, \"$strVariableToWiden\", \"$function\" );\n" .
			#		"\t\t\terr = JET_errOutOfMemory;\n" .
			#		"\t\t\tgoto Cleanup;\n" .
			#	"\t\t}\n" .
			#	"\n"
			#);
	}
	$handleOutput->print( "\t} // fWiden\n" );


}

sub PrintUnwidenSpecialVariable( $$$ )
#	$handleOutput
#	$refhashParamsToWiden
#	$refhashFunctionsToWiden
{
	my $handleOutput = shift @_;
	my $refhashParamsToWiden = shift @_;
	my $refhashFunctionsToWiden = shift @_;
	my $strVariables;

	foreach $strVariables ( keys( %$refhashParamsToWiden ) )
	{
		my $strType = $$refhashParamsToWiden{ $strVariables };
		my $function = $$refhashFunctionsToWiden{ $strType }[ 1 ];
		my $strVariableToWiden = "COULD_NOT_FIND_IT";
		if ( $strVariables =~ m/^([^,]+)/ )
		{
			$strVariableToWiden = $1;
		}

#		$function =~ s/Widen/Unwiden/;

		$handleOutput->print(
			"\n".
			"\tif ( fWiden )\n".
			"\t{\n".
				"\t\tconst JET_ERR errT = $function( __FUNCTION__, w$strVariables, $strVariableToWiden );\n".
				"\t\tif ( JET_errSuccess != errT )\n" .
				"\t\t{\n".
					"\t\t\ttprintf( \"%s():%d Failed to %s()\" CRLF, __FUNCTION__, __LINE__, \"$function\" );\n".
					"\t\t\tif ( JET_errSuccess == err )\n" .
					"\t\t\t{\n".
						"\t\t\t\t// Only overwrite err if it's not a success.\n".
						"\t\t\t\terr = errT;\n".
					"\t\t\t}\n".
				"\t\t}\n" .
			"\t}\n".
			"\n"
		);
	}


}


sub ProcessApi( $$$$$$$$$$ )
#	$staticCustomFileName
#	$loadCustomFileName
#	$hExportHeaderFile
#	$strApiType
#	$strApiName
#	$strApiSignature
#	\%hashParamsToWiden
#	\%hashFunctionsToWiden
#	\@rgstrApiParameters
#	\@rgstrApiParametersNoSal
#
{
	my $retCode	 = 0;

	my $handleStatic = shift @_;
	my $handleLoad = shift @_;
	my $hExportHeaderFile = shift @_;		# can be undef if you don't want it printed
	my $strApiType = shift @_;
	my $strApiName = shift @_;
	my $strApiSignature = shift @_;
	my $refhashParamsToWiden = shift @_;
	my $refhashFunctionsToWiden = shift @_;

	my $refrgstrApiParameters = shift @_;
	my $refrgstrApiParametersNoSal = shift @_;

	my @rgstrApiParameters = @$refrgstrApiParameters;
	my @rgstrApiParametersNoSal = @$refrgstrApiParametersNoSal;

	my $strApiWideSignature = $strApiSignature;
	$strApiWideSignature =~ s/char/WCHAR/g;
	$strApiWideSignature =~ s/JET_PSTR/JET_PWSTR/g;
	$strApiWideSignature =~ s/JET_PCSTR/JET_PCWSTR/g;
	$strApiWideSignature =~ s/sz/wsz/g;

	my $param;
	my @rgstrNarrowParams;

	my @rgBareParams;
	my $strBareParams = "";

	my $strParams = "";

	# Create a list of bare-parameters (no types);
	# and also a string of parameters (with types).
	foreach $param ( @rgstrApiParameters )
	{
		$strParams .= ",\n\t$param";
	}

	foreach $param ( @rgstrApiParametersNoSal )
	{
		$param =~ m/(\w+)$/;
		push @rgBareParams, $1;
		$strBareParams .= ", $1";
	}


	# Only remove the initial ", " if it was modified
	if ( $strBareParams ne "" )
	{
		$strBareParams =~ m/^, (.*)$/;
		$strBareParams = $1;
	}

	# Sometimetimes the signature is 'void' which makes it difficult to call.
	if ( $strBareParams =~ m/^\s*void\s*$/ )
	{
		 $strBareParams = "";
	}

	# Only remove the initial ", " if it was modified
	if ( $strParams ne "" )
	{
		$strParams =~ m/^,\n\t(.*)$/s;
		$strParams = $1;
	}

#	my $Opening = "\nJET_ERR\nBounce$strApiName(\n";
#	$handleStatic->print( $Opening );
#	$handleLoad->print( $Opening );
#
#	foreach $param ( @rgstrApiParameters )
#	{
#		$handleStatic->print( "\t$param,\n" );
#		$handleLoad->print( "\t$param,\n" );
#	}
#
#	my $strSigClose = ")\n{\n\tJET_ERR		err	= JET_errSuccess;\n";
#
#	$handleStatic->print( $strSigClose );
#	$handleLoad->print( $strSigClose );

	my $strPrototype = "\nJET_ERR\nBounce$strApiName(\n\t$strParams\n)";
	my $Opening = "$strPrototype\n{\n\tJET_ERR		err	= JET_errSuccess;\n";
	$handleStatic->print( $Opening );
	$handleLoad->print( $Opening );

	if ( defined( $hExportHeaderFile ) )
	{
		$hExportHeaderFile->print( "$strPrototype;\n" );
	}

	PrintLoadLibraryVars( $handleLoad, $strApiName, $strApiSignature );

	if ( $strApiType =~ m/widen/ )
	{

		PrintWidenLoadLibraryVars( $handleLoad, $strApiName, $strApiSignature, $strApiWideSignature );
		PrintWidenInitialization( $handleStatic );
		PrintWidenInitialization( $handleLoad );

		if ( %$refhashParamsToWiden )
		{
			PrintWidenSpecialVariable( $handleStatic, $refhashParamsToWiden, $refhashFunctionsToWiden );
			PrintWidenSpecialVariable( $handleLoad, $refhashParamsToWiden, $refhashFunctionsToWiden );
		}
		else
		{
			# Initialize @rgstrNarrowParams (stripping the type)
			@rgstrNarrowParams = grep( /char/, @rgstrApiParameters );
			foreach $param ( @rgstrNarrowParams )
			{
				$param =~ m/(\w+)$/;
				$param = $1;
			}

			foreach $param ( @rgstrNarrowParams )
			{
				PrintWidenDeclareVariable( $handleStatic, $param );
				PrintWidenDeclareVariableLength( $handleStatic, $param, $strBareParams );
				PrintWidenDeclareVariable( $handleLoad, $param );
				PrintWidenDeclareVariableLength( $handleLoad, $param, $strBareParams );
			}

			foreach $param ( @rgstrNarrowParams )
			{
				PrintWidenAllocateBufferAndCopy( $handleStatic, $param );
				PrintWidenAllocateBufferAndCopy( $handleLoad, $param );
			}
		}
	}

	if ( $strApiType =~ m/widen/ )
	{
		PrintStaticCallWiden( $handleStatic, $strApiName, $strBareParams );
		PrintLoadLibraryCallWiden( $handleLoad, $strApiName, $strBareParams, "" );
	}
	else
	{
		PrintStaticCall( $handleStatic, $strApiName, $strBareParams );
		PrintLoadLibraryCall( $handleLoad, $strApiName, $strBareParams, "" );
	}


	my $strCleanup = "\tgoto Cleanup;\t// Need to have the explicit goto in case the function hasn't referenced 'Cleanup' yet.\nCleanup:\n";
	$handleStatic->print( $strCleanup );
	$handleLoad->print( $strCleanup );


	if ( $strApiType =~ m/widen/ )
	{
		if ( %$refhashParamsToWiden )
		{
			PrintUnwidenSpecialVariable( $handleStatic, $refhashParamsToWiden, $refhashFunctionsToWiden );
			PrintUnwidenSpecialVariable( $handleLoad, $refhashParamsToWiden, $refhashFunctionsToWiden );
		}
		else
		{
			foreach $param ( @rgstrNarrowParams )
			{
				PrintWidenCleanupBuffer( $handleStatic, $param );
				PrintWidenCleanupBuffer( $handleLoad, $param );
			}
		}
	}

	my $strReturn = "\n\treturn err;\n}\n\n" .
	"//---------------------------------------------------\n\n";
	$handleStatic->print( $strReturn );
	$handleLoad->print( $strReturn );

	return $retCode;
}

sub PrintWidenInitialization()
{
	my $handle = shift @_;

	$handle->print( "\t// Should we widen the parameter?\n" .
	"\tbool\tfWiden = FEsetestWidenParameters();\n" .
	"\n"
	);
}

sub PrintWidenDeclareVariable()
{
	my $handle = shift @_;
	my $param = shift @_;

	$handle->print( "\twchar_t* w$param = NULL;\n"
	);
}

sub PrintWidenDeclareVariableLength()
{
	my $handle = shift @_;
	my $param = shift @_;
	my $strBareParams = shift @_;	# comma-sparated list of params (no types)

	my $paramWithoutType = "non_existent";

	# Extract 'Key' from 'szKey'
	if ( $strBareParams =~ m/^sz(\w+)$/ )
	{
		$paramWithoutType = $1;
	}

	# Is one of the parameters
	if ( $strBareParams =~ m/\b((cb|cch)$paramWithoutType)\b/ )
	{
		my $size = $1;
		$handle->print( "\tconst size_t cch${param}Size = fWiden ? $size : 0;\n" );
	}
	else
	{
		$handle->print( "\tconst size_t cch${param}Size = fWiden ? strlen( $param ) + 1 : 0;\n" );
	}

}

sub PrintWidenAllocateBufferAndCopy()
{
	my $handle = shift @_;
	my $param = shift @_;

	$handle->print(
			"\n" .
			"\t// Allocate and copy $param\n" .
			"\tw$param = new wchar_t[ cch${param}Size ];\n" .
			"\tif ( NULL == w$param )\n" .
			"\t{\n".
				"\t\ttprintf( __FUNCTION__ \"(): Failed to allocate memory for w$param (%Id bytes requested)\" CRLF, cch${param}Size );\n" .
				"\t\terr = JET_errOutOfMemory;\n" .
				"\t\tgoto Cleanup;\n" .
			"\t}\n" .
			"\thr = StringCchPrintfW( w$param, cch${param}Size, L\"%hs\", $param );\n" .
			"\tif ( FAILED( hr ) )\n" .
			"\t{\n".
				"\t\ttprintf( \"%s(): Failed to StringCchPrintfW(), hr = %d\" CRLF, __FUNCTION__, hr );\n" .
				"\t\terr = JET_errTestError;\n" .
				"\t\tgoto Cleanup;\n" .
			"\t}\n"
			);
}



sub PrintWidenCleanupBuffer()
{
	my $handle = shift @_;
	my $param = shift @_;

	$handle->print( "\tdelete[] w$param;\n\tw$param = NULL;\n" );
}


sub PrintLoadLibraryVars()
{
	my $handle = shift @_;
	my $strApiName = shift @_;
	my $strApiSignature = shift @_;

	$handle->print( "\n\ttypedef JET_ERR ( __stdcall *PFN_$strApiName ) ( $strApiSignature );\n\n" .
		"\tstatic PFN_$strApiName pfn$strApiName = NULL;\n"
	);
}

sub PrintWidenLoadLibraryVars()
{
	my $handle = shift @_;
	my $strApiName = shift @_;
	my $strApiSignature = shift @_;
	my $strApiWideSignature = shift @_;

	$handle->print(
		"\n".
		"\ttypedef JET_ERR ( __stdcall *PFN_${strApiName}A ) ( $strApiSignature );\n".
		"\n" .
		"\tstatic PFN_${strApiName}A pfn${strApiName}A = NULL;\n".
		"\n".
		"\ttypedef JET_ERR ( __stdcall *PFN_${strApiName}W ) ( $strApiWideSignature );\n".
		"\n" .
		"\tstatic PFN_${strApiName}W pfn${strApiName}W = NULL;\n"
	);
}

sub PrintStaticCall()
{
	my $handle = shift @_;
	my $strApiName = shift @_;
	my $strBareParams = shift @_;

	$handle->print( "\n\terr = $strApiName( $strBareParams );\n" );
}

sub PrintStaticCallWiden()
{
	my $handle = shift @_;
	my $strApiName = shift @_;
	my $strBareParams = shift @_;

	my $strBareParamsWidened = $strBareParams;
	$strBareParamsWidened =~ s/sz/wsz/g;

	$handle->print(
	"\n\tif ( fWiden )\n" .
	"\t{\n".
		"\t\terr = ${strApiName}W( $strBareParamsWidened );\n" .
	"\t}\n" .
	"\telse\n" .
	"\t{\n" .
		"\t\t// Call the legacy API sometimes, call the 'A' API others.\n" .
		"\t\tif ( 0 == ( rand() % 3 ) )\n" .
		"\t\t{\n" .
			"\t\t\terr = $strApiName( $strBareParams );\n" .
		"\t\t}\n" .
		"\t\telse\n" .
		"\t\t{\n" .
			"\t\t\terr = ${strApiName}A( $strBareParams );\n" .
		"\t\t}\n" .
	"\t}\n"
	);
}


sub PrintLoadLibraryCall($$$$)
{
	my $handle = shift @_;
	my $strApiName = shift @_;
	my $strBareParams = shift @_;
	my $strAdditionalIndent = shift @_;

	$handle->print(
	"\n" .
	"$strAdditionalIndent\t// Get the procedure address if this is the first time calling this function.\n" .
	"$strAdditionalIndent\tif ( NULL == pfn$strApiName )\n" .
	"$strAdditionalIndent\t{\n" .
		"$strAdditionalIndent\t\t// Do not bother with synchronization of GetProcAddress() -- sloppy, but a leak isn't a big deal.\n" .
		"$strAdditionalIndent\t\tconst HMODULE\t\thEseDll = HmodEsetestEseDll();\n\n" .
		"$strAdditionalIndent\t\tif ( NULL != hEseDll )\n" .
		"$strAdditionalIndent\t\t{\n" .
#			"$strAdditionalIndent\t\t\tpfn$strApiName = static_cast< PFN_$strApiName >( GetProcAddress( hEseDll, \"$strApiName\" ) );\n" .
			"$strAdditionalIndent\t\t\tpfn$strApiName = ( PFN_$strApiName ) ( GetProcAddress( hEseDll, \"$strApiName\" ) );\n" .
		"$strAdditionalIndent\t\t}\n" .
		"$strAdditionalIndent\t\tif ( NULL == hEseDll || NULL == pfn$strApiName )\n" .
		"$strAdditionalIndent\t\t{\n" .
		"$strAdditionalIndent\t\t\ttprintf( \"%s(): Failed to either fetch hEseDll (=%p) GetProcAddress( hEseDll, %s ), Gle = %d \" CRLF,\n".
		"$strAdditionalIndent\t\t\t\t__FUNCTION__, hEseDll, \"$strApiName\", GetLastError() );\n" .
		"$strAdditionalIndent\t\t\terr = JET_errTestError;\n" .
		"$strAdditionalIndent\t\t\tgoto Cleanup;\n" .
		"$strAdditionalIndent\t\t}\n" .
	"$strAdditionalIndent\t}\n" .
	"\n" .
	"$strAdditionalIndent\terr = (*pfn$strApiName)( $strBareParams );\n"
	);


}

sub PrintLoadLibraryCallWiden()
{
	my $handle = shift @_;
	my $strApiName = shift @_;
	my $strBareParams = shift @_;
	my $strAdditionalIndent = shift @_;

	my $strBareParamsWidened = $strBareParams;
	$strBareParamsWidened =~ s/sz/wsz/g;

	$handle->print(
		"\n".
		"\tif ( fWiden )\n" .
		"\t{\n"
	);
	PrintLoadLibraryCall( $handle, "${strApiName}W", $strBareParamsWidened, "\t" );
	$handle->print(
		"\t}\n" .
		"\telse\n" .
		"\t{\n" .
			"\t\t// Call the legacy API sometimes, call the 'A' API others (if available).\n" .
			"\t\tif ( ( !FEsetestFeaturePresent( EseFeatureApisExportedWithA ) )  || 0 == ( rand() % 3 ) )\n" .
			"\t\t{\n"
		);
		# come back here
	PrintLoadLibraryCall( $handle, "${strApiName}", $strBareParams, "\t\t" );
	$handle->print(
			"\t\t}\n" .
			"\t\telse\n" .
			"\t\t{\n"
		);
	PrintLoadLibraryCall( $handle, "${strApiName}A", $strBareParams, "\t\t" );
	$handle->print(
			"\t\t}\n" .
		"\t}\n"
	);
}


